﻿//----------------------------------------------------------------
// Copyright (c) 2011-2013 
// All rights reserved.
// Author: Ben DaoWei 
// Contact: bendaowei@gmail.com
// Web Link: http://xnaeditor.codeplex.com
// ----------------------------------------------------------------

namespace UnityEngine.IO
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using Reflector;
    using Microsoft.Xna.Framework;

    public class Serializer
    {
        private static Dictionary<Type, XMLDeserialize> deserializeMethods = new Dictionary<Type, XMLDeserialize>();

        private static Dictionary<Type, XMlSerialize> serializeMethods = new Dictionary<Type, XMlSerialize>();

        public delegate void XMLDeserialize(ref object target, XmlNode reader);

        public delegate void XMlSerialize(ref object target, XmlWriter writer);

        public static void Register(Type type, XMlSerialize serialize, XMLDeserialize deserialize)
        {
            serializeMethods.Add(type, serialize);
            deserializeMethods.Add(type, deserialize);
        }

        public void Serialize(string tag, object target, XmlWriter writer, Type type)
        {
            writer.WriteStartElement(tag);
            if (type.IsAtomic())
            {
                if (target == null)
                    writer.WriteString("");
                else
                    writer.WriteString(target.ToString());
            }
            else
            {
                XmlSerialize(target, writer, type);
            }
            writer.WriteFullEndElement();
        }

        public object Deserialize(string tag, XmlNode root, Type type)
        {
            try
            {
                object ret = null;
                if (root.Name != tag)
                    throw new Exception("Error");
                if (type.IsAtomic())
                {
                    string value = root.InnerText;
                    if (ret == null && type == typeof(string))
                        ret = value;
                    if (ret == null && type == typeof(float))
                        ret = float.Parse(value);
                    if (ret == null && type == typeof(int))
                        ret = int.Parse(value);
                    if (ret == null && type == typeof(bool))
                        ret = bool.Parse(value);
                    if (ret == null && Enum.IsDefined(type, value))
                        ret = Enum.Parse(type, value);
                    //if (ret == null)
                    //    throw new Exception("Type not supported");
                }
                else
                {
                    ret = Activator.CreateInstance(type);
                    XmlDeserialize(ref ret, root, type);
                }
                return ret;
            }
            catch (System.Exception e)
            {
                return null;
            }
        }

        private void XmlSerialize(object target, XmlWriter writer, Type type = null)
        {
            if (type == null)
                type = target.GetType();
            if (serializeMethods.ContainsKey(type) && serializeMethods[type] != null)
            {
                //serializeMethods[type](ref target, writer);
                return;
            }
            else
            {
                if (target is IXmlSerializable)
                {
                    //(target as IXmlSerializable).WriteXml(doc, root);
                    return;
                }

                if (type == typeof(Vector3))
                {
                    Vector3 t = (Vector3)target;
                    this.Serialize("x", t.X, writer, typeof(float));
                    this.Serialize("y", t.Y, writer, typeof(float));
                    this.Serialize("z", t.Z, writer, typeof(float));
                }

                if (type == typeof(Vector2))
                {
                    Vector2 t = (Vector2)target;
                    this.Serialize("x", t.X, writer, typeof(float));
                    this.Serialize("y", t.Y, writer, typeof(float));
                }

                if (type == typeof(Vector4))
                {
                    Vector4 t = (Vector4)target;
                    this.Serialize("x", t.X, writer, typeof(float));
                    this.Serialize("y", t.Y, writer, typeof(float));
                    this.Serialize("z", t.Z, writer, typeof(float));
                    this.Serialize("w", t.W, writer, typeof(float));
                }
            }
        }

        private void XmlDeserialize(ref object target, XmlNode root, Type type = null)
        {
            if (type == null)
                type = target.GetType();
            if (deserializeMethods.ContainsKey(type) && deserializeMethods[type] != null)
            {
                deserializeMethods[type](ref target, root);
                return;
            }
            else
            {
                if (type == typeof(Vector3))
                {
                    Vector3 temp = (Vector3)target;
                    temp.X = (float)this.Deserialize("x", root.SelectSingleNode("./x"), typeof(float));
                    temp.Y = (float)this.Deserialize("y", root.SelectSingleNode("./y"), typeof(float));
                    temp.Z = (float)this.Deserialize("z", root.SelectSingleNode("./z"), typeof(float));
                    target = temp;
                }

                if (type == typeof(Vector2))
                {
                    Vector2 temp = (Vector2)target;
                    temp.X = (float)this.Deserialize("x", root.SelectSingleNode("./x"), typeof(float));
                    temp.Y = (float)this.Deserialize("y", root.SelectSingleNode("./y"), typeof(float));
                    target = temp;
                }

                if (type == typeof(Vector4))
                {
                    Vector4 temp = (Vector4)target;
                    temp.X = (float)this.Deserialize("x", root.SelectSingleNode("./x"), typeof(float));
                    temp.Y = (float)this.Deserialize("y", root.SelectSingleNode("./y"), typeof(float));
                    temp.Z = (float)this.Deserialize("z", root.SelectSingleNode("./z"), typeof(float));
                    temp.W = (float)this.Deserialize("w", root.SelectSingleNode("./w"), typeof(float));
                    target = temp;
                }
            }
        }
    }
}
